/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.rational.parser.expression;

import java.util.ArrayList;
import java.util.Collections;

/**
 *
 * @author Overmind
 */
public class Expression
        implements Comparable<Expression>
{
    public static final String DEFAULT_ARGUMENT_SYMBOL = "?";
    
    private ArrayList<Delim> delims;
    
    private String expression;
    private String argumentSymbol;
    private boolean argumentFirst;
    private int argumentCount = 0;

    
    {
        delims = new ArrayList<>();
    }

    public Expression(String expression)
            throws NullPointerException
    {
        this(expression, DEFAULT_ARGUMENT_SYMBOL);
    }

    public Expression(String expression, String argumentSymbol) 
            throws NullPointerException
    {
        if(expression == null)
        {
            throw new NullPointerException("expressin most be not null");
        }
        
        if(argumentSymbol == null)
        {
            throw new NullPointerException("argumentSymbol most be not null");
        }
        
        this.argumentSymbol = argumentSymbol;
        this.expression = expression;
        init();
    }

    private void init()
    {
        int find = 0;
        boolean checkRepeat = false;

        String data = expression;
        argumentFirst = data.startsWith(argumentSymbol);

        while (find != -1)
        {
            find = data.indexOf(argumentSymbol);

            if (find >= 0)
            {
                ++argumentCount;

                if (find == 0)
                {
                    if (checkRepeat)
                    {
                        throw new IllegalArgumentException("Argument symbol can`t be repeat");
                    } 
                    else
                    {
                        checkRepeat = true;
                    }

                } 
                else
                {
                    if (find > 0)
                    {
                        checkRepeat = false;
                        delims.add(new Delim(data.substring(0, find)));
                    }
                }

                data = data.substring(find + argumentSymbol.length());
            }
        }

        if (!data.isEmpty())
        {
            delims.add(new Delim(data));
        }
    }

    public int getArgumentCount()
    {
        return argumentCount;
    }

    public int getDelimCount()
    {
        return delims.size();
    }

    private ArrayList<String[]> getWays(String data, int delimNumber, int remain)
    {
        ArrayList<String[]> result = new ArrayList<>();
        
        if(delimNumber < getDelimCount())
        {
            Skope[] skopes = delims.get(delimNumber).getSkopes(data);
        
            for (int i=0;i<skopes.length;++i)
            {
                Skope skope = skopes[i];
                
                int shift = 0;
                String[] strings = new String[remain];
                
                if(skope.getBegin() > 0)
                {
                    if(delimNumber == 0 && !argumentFirst)
                    {
                        continue;
                    }
                    
                    strings[shift++] = data.substring(0, skope.getBegin());
                }
                
                ArrayList<String[]> sub_result = 
                        getWays(data.substring(skope.getEnd()), 
                        delimNumber + 1, remain - shift);
                    
                if(sub_result != null)
                {
                    for (String[] sub_strings : sub_result)
                    {
                        for (int j=0;j<remain-shift;++j)
                        {
                            strings[j+shift] = sub_strings[j];
                        }
                    }
                }
                
                boolean initialized = true;
                for (String string : strings)
                {
                    if(string == null)
                    {
                        initialized = false;
                    }
                }
                
                if(initialized)
                {
                    result.add(strings);
                }
            }

            return result;
        }
        else if (remain == 1)
        {
            result.add(new String[] {data});
            --remain;
        }
        
        return result;
    }
    
    public ArrayList<String[]> getPossibleArguments(String formulas) 
        throws NullPointerException
    {
        if(formulas == null)
        {
            throw new NullPointerException("formulas most be not null");
        }
        
        ArrayList<String[]> ways = getWays(formulas, 0, getArgumentCount());
        Collections.reverse(ways);    
        
        return ways;
    }

    @Override
    public int compareTo(Expression right)
    {
        int argumentFirstCompare = Boolean.compare(argumentFirst, right.argumentFirst);

        if (argumentFirstCompare == 0)
        {
            int argumentCountCompare = Integer.compare(argumentCount, right.argumentCount);

            if (argumentCountCompare == 0)
            {
                int size = delims.size();
                for (int i = 0; i < size; ++i)
                {
                    int DelimCompare = delims.get(i).compareTo(right.delims.get(i));

                    if (DelimCompare != 0)
                    {
                        return DelimCompare;
                    }
                }

                return 0;
            } else
            {
                return argumentCountCompare;
            }
        } else
        {
            return argumentFirstCompare;
        }
    }

    @Override
    public String toString()
    {
        return expression;
    }
}
